home *** CD-ROM | disk | FTP | other *** search
/ Clickx 63 / Clickx 63.iso / software / multimedia / mirov204 / Miro_Installer.exe / xulrunner / chrome / toolkit.jar / content / global / contentAreaUtils.js < prev    next >
Encoding:
JavaScript  |  2007-12-19  |  33.0 KB  |  951 lines

  1. //@line 40 "/e/xr19rel/WINNT_5.2_Depend/mozilla/toolkit/content/contentAreaUtils.js"
  2.  
  3. /**
  4.  * urlSecurityCheck: JavaScript wrapper for checkLoadURIWithPrincipal
  5.  * and checkLoadURIStrWithPrincipal.
  6.  * If |aPrincipal| is not allowed to link to |aURL|, this function throws with
  7.  * an error message.
  8.  *
  9.  * @param aURL
  10.  *        The URL a page has linked to. This could be passed either as a string
  11.  *        or as a nsIURI object.
  12.  * @param aPrincipal
  13.  *        The principal of the document from which aURL came.
  14.  * @param aFlags
  15.  *        Flags to be passed to checkLoadURIStr. If undefined,
  16.  *        nsIScriptSecurityManager.STANDARD will be passed.
  17.  */
  18. function urlSecurityCheck(aURL, aPrincipal, aFlags)
  19. {
  20.   const nsIScriptSecurityManager =
  21.     Components.interfaces.nsIScriptSecurityManager;
  22.   var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
  23.                          .getService(nsIScriptSecurityManager);
  24.   if (aFlags === undefined)
  25.     aFlags = nsIScriptSecurityManager.STANDARD;
  26.  
  27.   try {
  28.     if (aURL instanceof Components.interfaces.nsIURI)
  29.       secMan.checkLoadURIWithPrincipal(aPrincipal, aURL, aFlags);
  30.     else
  31.       secMan.checkLoadURIStrWithPrincipal(aPrincipal, aURL, aFlags);
  32.   } catch (e) {
  33.     // XXXmano: dump the principal url here too
  34.     throw "Load of " + aURL + " denied.";
  35.   }
  36. }
  37.  
  38. /**
  39.  * Determine whether or not a given focused DOMWindow is in the content area.
  40.  **/
  41. function isContentFrame(aFocusedWindow)
  42. {
  43.   if (!aFocusedWindow)
  44.     return false;
  45.  
  46.   return (aFocusedWindow.top == window.content);
  47. }
  48.  
  49.  
  50. // Clientelle: (Make sure you don't break any of these)
  51. //  - File    ->  Save Page/Frame As...
  52. //  - Context ->  Save Page/Frame As...
  53. //  - Context ->  Save Link As...
  54. //  - Alt-Click links in web pages
  55. //  - Alt-Click links in the UI
  56. //
  57. // Try saving each of these types:
  58. // - A complete webpage using File->Save Page As, and Context->Save Page As
  59. // - A webpage as HTML only using the above methods
  60. // - A webpage as Text only using the above methods
  61. // - An image with an extension (e.g. .jpg) in its file name, using
  62. //   Context->Save Image As...
  63. // - An image without an extension (e.g. a banner ad on cnn.com) using
  64. //   the above method.
  65. // - A linked document using Save Link As...
  66. // - A linked document using Alt-click Save Link As...
  67. //
  68. function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
  69.                  aSkipPrompt, aReferrer)
  70. {
  71.   internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
  72.                aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
  73. }
  74.  
  75. // Just like saveURL, but will get some info off the image before
  76. // calling internalSave
  77. // Clientelle: (Make sure you don't break any of these)
  78. //  - Context ->  Save Image As...
  79. const imgICache = Components.interfaces.imgICache;
  80. const nsISupportsCString = Components.interfaces.nsISupportsCString;
  81.  
  82. function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
  83.                       aSkipPrompt, aReferrer)
  84. {
  85.   var contentType = null;
  86.   var contentDisposition = null;
  87.   if (!aShouldBypassCache) {
  88.     try {
  89.       var imageCache = Components.classes["@mozilla.org/image/cache;1"]
  90.                                  .getService(imgICache);
  91.       var props =
  92.         imageCache.findEntryProperties(makeURI(aURL, getCharsetforSave(null)));
  93.       if (props) {
  94.         contentType = props.get("type", nsISupportsCString);
  95.         contentDisposition = props.get("content-disposition",
  96.                                        nsISupportsCString);
  97.       }
  98.     } catch (e) {
  99.       // Failure to get type and content-disposition off the image is non-fatal
  100.     }
  101.   }
  102.   internalSave(aURL, null, aFileName, contentDisposition, contentType,
  103.                aShouldBypassCache, aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
  104. }
  105.  
  106. function saveFrameDocument()
  107. {
  108.   var focusedWindow = document.commandDispatcher.focusedWindow;
  109.   if (isContentFrame(focusedWindow))
  110.     saveDocument(focusedWindow.document);
  111. }
  112.  
  113. function saveDocument(aDocument, aSkipPrompt)
  114. {
  115.   if (!aDocument)
  116.     throw "Must have a document when calling saveDocument";
  117.  
  118.   // We want to use cached data because the document is currently visible.
  119.   var contentDisposition = null;
  120.   try {
  121.     contentDisposition =
  122.       aDocument.defaultView
  123.                .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  124.                .getInterface(Components.interfaces.nsIDOMWindowUtils)
  125.                .getDocumentMetadata("content-disposition");
  126.   } catch (ex) {
  127.     // Failure to get a content-disposition is ok
  128.   }
  129.   internalSave(aDocument.location.href, aDocument, null, contentDisposition,
  130.                aDocument.contentType, false, null, null,
  131.                aDocument.referrer ? makeURI(aDocument.referrer) : null,
  132.                aSkipPrompt);
  133. }
  134.  
  135. function DownloadListener(win, transfer) {
  136.   function makeClosure(name) {
  137.     return function() {
  138.       transfer[name].apply(transfer, arguments);
  139.     }
  140.   }
  141.  
  142.   this.window = win;
  143.  
  144.   // Now... we need to forward all calls to our transfer
  145.   for (var i in transfer) {
  146.     if (i != "QueryInterface")
  147.       this[i] = makeClosure(i);
  148.   }
  149. }
  150.  
  151. DownloadListener.prototype = {
  152.   QueryInterface: function dl_qi(aIID)
  153.   {
  154.     if (aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
  155.         aIID.equals(Components.interfaces.nsIWebProgressListener) ||
  156.         aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
  157.         aIID.equals(Components.interfaces.nsISupports)) {
  158.       return this;
  159.     }
  160.     throw Components.results.NS_ERROR_NO_INTERFACE;
  161.   },
  162.  
  163.   getInterface: function dl_gi(aIID)
  164.   {
  165.     if (aIID.equals(Components.interfaces.nsIAuthPrompt) ||
  166.         aIID.equals(Components.interfaces.nsIAuthPrompt2)) {
  167.       var ww =
  168.         Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  169.                   .getService(Components.interfaces.nsIPromptFactory);
  170.       return ww.getPrompt(this.window, aIID);
  171.     }
  172.  
  173.     throw Components.results.NS_ERROR_NO_INTERFACE;
  174.   }
  175. }
  176.  
  177. const kSaveAsType_Complete = 0; // Save document with attached objects.
  178. // const kSaveAsType_URL      = 1; // Save document or URL by itself.
  179. const kSaveAsType_Text     = 2; // Save document, converting to plain text.
  180.  
  181. /**
  182.  * internalSave: Used when saving a document or URL. This method:
  183.  *  - Determines a local target filename to use (unless parameter
  184.  *    aChosenData is non-null)
  185.  *  - Determines content-type if possible
  186.  *  - Prompts the user to confirm the destination filename and save mode
  187.  *    (content-type affects this)
  188.  *  - Creates a 'Persist' object (which will perform the saving in the
  189.  *    background) and then starts it.
  190.  *
  191.  * @param aURL The String representation of the URL of the document being saved
  192.  * @param aDocument The document to be saved
  193.  * @param aDefaultFileName The caller-provided suggested filename if we don't
  194.  *        find a better one
  195.  * @param aContentDisposition The caller-provided content-disposition header
  196.  *         to use.
  197.  * @param aContentType The caller-provided content-type to use
  198.  * @param aShouldBypassCache If true, the document will always be refetched
  199.  *        from the server
  200.  * @param aFilePickerTitleKey Alternate title for the file picker
  201.  * @param aChosenData If non-null this contains an instance of object AutoChosen
  202.  *        (see below) which holds pre-determined data so that the user does not
  203.  *        need to be prompted for a target filename.
  204.  * @param aReferrer the referrer URI object (not URL string) to use, or null
  205.           if no referrer should be sent.
  206.  * @param aSkipPrompt If true, the file will be saved to the default download folder.
  207.  */
  208. function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
  209.                       aContentType, aShouldBypassCache, aFilePickerTitleKey,
  210.                       aChosenData, aReferrer, aSkipPrompt)
  211. {
  212.   if (aSkipPrompt == undefined)
  213.     aSkipPrompt = false;
  214.  
  215.   // Note: aDocument == null when this code is used by save-link-as...
  216.   var saveMode = GetSaveModeForContentType(aContentType);
  217.   var isDocument = aDocument != null && saveMode != SAVEMODE_FILEONLY;
  218.   var saveAsType = kSaveAsType_Complete;
  219.  
  220.   var file, fileURL;
  221.   // Find the URI object for aURL and the FileName/Extension to use when saving.
  222.   // FileName/Extension will be ignored if aChosenData supplied.
  223.   var fileInfo = new FileInfo(aDefaultFileName);
  224.   if (aChosenData)
  225.     file = aChosenData.file;
  226.   else {
  227.     var charset = null;
  228.     if (aDocument)
  229.       charset = aDocument.characterSet;
  230.     else if (aReferrer)
  231.       charset = aReferrer.originCharset;
  232.     initFileInfo(fileInfo, aURL, charset, aDocument,
  233.                  aContentType, aContentDisposition);
  234.     var fpParams = {
  235.       fpTitleKey: aFilePickerTitleKey,
  236.       isDocument: isDocument,
  237.       fileInfo: fileInfo,
  238.       contentType: aContentType,
  239.       saveMode: saveMode,
  240.       saveAsType: saveAsType,
  241.       file: file,
  242.       fileURL: fileURL
  243.     };
  244.  
  245.     if (!getTargetFile(fpParams, aSkipPrompt))
  246.       // If the method returned false this is because the user cancelled from
  247.       // the save file picker dialog.
  248.       return;
  249.  
  250.     saveAsType = fpParams.saveAsType;
  251.     saveMode = fpParams.saveMode;
  252.     file = fpParams.file;
  253.     fileURL = fpParams.fileURL;
  254.   }
  255.  
  256.   if (!fileURL)
  257.     fileURL = makeFileURI(file);
  258.  
  259.   // XXX We depend on the following holding true in appendFiltersForContentType():
  260.   // If we should save as a complete page, the saveAsType is kSaveAsType_Complete.
  261.   // If we should save as text, the saveAsType is kSaveAsType_Text.
  262.   var useSaveDocument = isDocument &&
  263.                         (((saveMode & SAVEMODE_COMPLETE_DOM) && (saveAsType == kSaveAsType_Complete)) ||
  264.                          ((saveMode & SAVEMODE_COMPLETE_TEXT) && (saveAsType == kSaveAsType_Text)));
  265.   // If we're saving a document, and are saving either in complete mode or
  266.   // as converted text, pass the document to the web browser persist component.
  267.   // If we're just saving the HTML (second option in the list), send only the URI.
  268.   var source = useSaveDocument ? aDocument : fileInfo.uri;
  269.   var persistArgs = {
  270.     source      : source,
  271.     contentType : (!aChosenData && useSaveDocument &&
  272.                    saveAsType == kSaveAsType_Text) ?
  273.                   "text/plain" : null,
  274.     target      : fileURL,
  275.     postData    : isDocument ? getPostData() : null,
  276.     bypassCache : aShouldBypassCache
  277.   };
  278.  
  279.   var persist = makeWebBrowserPersist();
  280.  
  281.   // Calculate persist flags.
  282.   const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
  283.   const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
  284.   if (aShouldBypassCache)
  285.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
  286.   else
  287.     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
  288.  
  289.   // Leave it to WebBrowserPersist to discover the encoding type (or lack thereof):
  290.   persist.persistFlags |= nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
  291.  
  292.   // Create download and initiate it (below)
  293.   var tr = Components.classes["@mozilla.org/transfer;1"].createInstance(Components.interfaces.nsITransfer);
  294.  
  295.   if (useSaveDocument) {
  296.     // Saving a Document, not a URI:
  297.     var filesFolder = null;
  298.     if (persistArgs.contentType != "text/plain") {
  299.       // Create the local directory into which to save associated files.
  300.       filesFolder = file.clone();
  301.  
  302.       var nameWithoutExtension = getFileBaseName(filesFolder.leafName);
  303.       var filesFolderLeafName = getStringBundle().formatStringFromName("filesFolder",
  304.                                                                        [nameWithoutExtension],
  305.                                                                        1);
  306.  
  307.       filesFolder.leafName = filesFolderLeafName;
  308.     }
  309.  
  310.     var encodingFlags = 0;
  311.     if (persistArgs.contentType == "text/plain") {
  312.       encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
  313.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
  314.       encodingFlags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;
  315.     }
  316.     else {
  317.       encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
  318.     }
  319.  
  320.     const kWrapColumn = 80;
  321.     tr.init((aChosenData ? aChosenData.uri : fileInfo.uri),
  322.             persistArgs.target, "", null, null, null, persist);
  323.     persist.progressListener = new DownloadListener(window, tr);
  324.     persist.saveDocument(persistArgs.source, persistArgs.target, filesFolder,
  325.                          persistArgs.contentType, encodingFlags, kWrapColumn);
  326.   } else {
  327.     tr.init((aChosenData ? aChosenData.uri : source),
  328.             persistArgs.target, "", null, null, null, persist);
  329.     persist.progressListener = new DownloadListener(window, tr);
  330.     persist.saveURI((aChosenData ? aChosenData.uri : source),
  331.                     null, aReferrer, persistArgs.postData, null,
  332.                     persistArgs.target);
  333.   }
  334. }
  335.  
  336. /**
  337.  * Structure for holding info about automatically supplied parameters for
  338.  * internalSave(...). This allows parameters to be supplied so the user does not
  339.  * need to be prompted for file info.
  340.  * @param aFileAutoChosen This is an nsILocalFile object that has been
  341.  *        pre-determined as the filename for the target to save to
  342.  * @param aUriAutoChosen  This is the nsIURI object for the target
  343.  */
  344. function AutoChosen(aFileAutoChosen, aUriAutoChosen) {
  345.   this.file = aFileAutoChosen;
  346.   this.uri  = aUriAutoChosen;
  347. }
  348.  
  349. /**
  350.  * Structure for holding info about a URL and the target filename it should be
  351.  * saved to. This structure is populated by initFileInfo(...).
  352.  * @param aSuggestedFileName This is used by initFileInfo(...) when it
  353.  *        cannot 'discover' the filename from the url 
  354.  * @param aFileName The target filename
  355.  * @param aFileBaseName The filename without the file extension
  356.  * @param aFileExt The extension of the filename
  357.  * @param aUri An nsIURI object for the url that is being saved
  358.  */
  359. function FileInfo(aSuggestedFileName, aFileName, aFileBaseName, aFileExt, aUri) {
  360.   this.suggestedFileName = aSuggestedFileName;
  361.   this.fileName = aFileName;
  362.   this.fileBaseName = aFileBaseName;
  363.   this.fileExt = aFileExt;
  364.   this.uri = aUri;
  365. }
  366.  
  367. /**
  368.  * Determine what the 'default' filename string is, its file extension and the
  369.  * filename without the extension. This filename is used when prompting the user
  370.  * for confirmation in the file picker dialog.
  371.  * @param aFI A FileInfo structure into which we'll put the results of this method.
  372.  * @param aURL The String representation of the URL of the document being saved
  373.  * @param aURLCharset The charset of aURL.
  374.  * @param aDocument The document to be saved
  375.  * @param aContentType The content type we're saving, if it could be
  376.  *        determined by the caller.
  377.  * @param aContentDisposition The content-disposition header for the object
  378.  *        we're saving, if it could be determined by the caller.
  379.  */
  380. function initFileInfo(aFI, aURL, aURLCharset, aDocument,
  381.                       aContentType, aContentDisposition)
  382. {
  383.   try {
  384.     // Get an nsIURI object from aURL if possible:
  385.     try {
  386.       aFI.uri = makeURI(aURL, aURLCharset);
  387.       // Assuming nsiUri is valid, calling QueryInterface(...) on it will
  388.       // populate extra object fields (eg filename and file extension).
  389.       var url = aFI.uri.QueryInterface(Components.interfaces.nsIURL);
  390.       aFI.fileExt = url.fileExtension;
  391.     } catch (e) {
  392.     }
  393.  
  394.     // Get the default filename:
  395.     aFI.fileName = getDefaultFileName((aFI.suggestedFileName || aFI.fileName),
  396.                                       aFI.uri, aDocument, aContentDisposition);
  397.     // If aFI.fileExt is still blank, consider: aFI.suggestedFileName is supplied
  398.     // if saveURL(...) was the original caller (hence both aContentType and
  399.     // aDocument are blank). If they were saving a link to a website then make
  400.     // the extension .htm .
  401.     if (!aFI.fileExt && !aDocument && !aContentType && (/^http(s?):\/\//i.test(aURL))) {
  402.       aFI.fileExt = "htm";
  403.       aFI.fileBaseName = aFI.fileName;
  404.     } else {
  405.       aFI.fileExt = getDefaultExtension(aFI.fileName, aFI.uri, aContentType);
  406.       aFI.fileBaseName = getFileBaseName(aFI.fileName);
  407.     }
  408.   } catch (e) {
  409.   }
  410. }
  411.  
  412. function getTargetFile(aFpP, aSkipPrompt)
  413. {
  414.   const prefSvcContractID = "@mozilla.org/preferences-service;1";
  415.   const prefSvcIID = Components.interfaces.nsIPrefService;                              
  416.   var prefs = Components.classes[prefSvcContractID]
  417.                         .getService(prefSvcIID).getBranch("browser.download.");
  418.  
  419.   const nsILocalFile = Components.interfaces.nsILocalFile;
  420.  
  421.   // For information on download folder preferences, see
  422.   // mozilla/browser/components/preferences/main.js
  423.   
  424.   var useDownloadDir = prefs.getBoolPref("useDownloadDir");
  425.   var dir = null;
  426.   
  427.   // Default to lastDir if useDownloadDir is false, and lastDir
  428.   // is configured and valid. Otherwise, use the user's default
  429.   // downloads directory configured through download prefs.
  430.   var dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
  431.                           .getService(Components.interfaces.nsIDownloadManager);
  432.   try {                          
  433.     var lastDir = prefs.getComplexValue("lastDir", nsILocalFile);
  434.     if ((!aSkipPrompt || !useDownloadDir) && lastDir.exists())
  435.       dir = lastDir;
  436.     else
  437.       dir = dnldMgr.userDownloadsDirectory;
  438.   } catch(ex) {
  439.     dir = dnldMgr.userDownloadsDirectory;
  440.   }
  441.  
  442.   if (!aSkipPrompt || !useDownloadDir || !dir || (dir && !dir.exists())) {
  443.     if (!dir || (dir && !dir.exists())) {
  444.       // Default to desktop.
  445.       var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
  446.                                   .getService(Components.interfaces.nsIProperties);
  447.       dir = fileLocator.get("Desk", nsILocalFile);
  448.     }
  449.  
  450.     var fp = makeFilePicker();
  451.     var titleKey = aFpP.fpTitleKey || "SaveLinkTitle";
  452.     var bundle = getStringBundle();
  453.     fp.init(window, bundle.GetStringFromName(titleKey), 
  454.             Components.interfaces.nsIFilePicker.modeSave);
  455.     
  456.     fp.defaultExtension = aFpP.fileInfo.fileExt;
  457.     fp.defaultString = getNormalizedLeafName(aFpP.fileInfo.fileName,
  458.                                              aFpP.fileInfo.fileExt);
  459.     appendFiltersForContentType(fp, aFpP.contentType, aFpP.fileInfo.fileExt,
  460.                                 aFpP.saveMode);
  461.  
  462.     if (dir)
  463.       fp.displayDirectory = dir;
  464.     
  465.     if (aFpP.isDocument) {
  466.       try {
  467.         fp.filterIndex = prefs.getIntPref("save_converter_index");
  468.       }
  469.       catch (e) {
  470.       }
  471.     }
  472.  
  473.     if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
  474.       return false;
  475.     
  476.     var directory = fp.file.parent.QueryInterface(nsILocalFile);
  477.     prefs.setComplexValue("lastDir", nsILocalFile, directory);
  478.  
  479.     fp.file.leafName = validateFileName(fp.file.leafName);
  480.     aFpP.saveAsType = fp.filterIndex;
  481.     aFpP.file = fp.file;
  482.     aFpP.fileURL = fp.fileURL;
  483.  
  484.     if (aFpP.isDocument)
  485.       prefs.setIntPref("save_converter_index", aFpP.saveAsType);
  486.   }
  487.   else {
  488.     dir.append(getNormalizedLeafName(aFpP.fileInfo.fileName,
  489.                                      aFpP.fileInfo.fileExt));
  490.     var file = dir;
  491.     
  492.     // Since we're automatically downloading, we don't get the file picker's 
  493.     // logic to check for existing files, so we need to do that here.
  494.     //
  495.     // Note - this code is identical to that in
  496.     //   mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
  497.     // If you are updating this code, update that code too! We can't share code
  498.     // here since that code is called in a js component.
  499.     var collisionCount = 0;
  500.     while (file.exists()) {
  501.       collisionCount++;
  502.       if (collisionCount == 1) {
  503.         // Append "(2)" before the last dot in (or at the end of) the filename
  504.         // special case .ext.gz etc files so we don't wind up with .tar(2).gz
  505.         if (file.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
  506.           file.leafName = file.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
  507.         else
  508.           file.leafName = file.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
  509.       }
  510.       else {
  511.         // replace the last (n) in the filename with (n+1)
  512.         file.leafName = file.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
  513.       }
  514.     }
  515.     aFpP.file = file;
  516.   }
  517.  
  518.   return true;
  519. }
  520.  
  521. // We have no DOM, and can only save the URL as is.
  522. const SAVEMODE_FILEONLY      = 0x00;
  523. // We have a DOM and can save as complete.
  524. const SAVEMODE_COMPLETE_DOM  = 0x01;
  525. // We have a DOM which we can serialize as text.
  526. const SAVEMODE_COMPLETE_TEXT = 0x02;
  527.  
  528. // If we are able to save a complete DOM, the 'save as complete' filter
  529. // must be the first filter appended.  The 'save page only' counterpart
  530. // must be the second filter appended.  And the 'save as complete text'
  531. // filter must be the third filter appended.
  532. function appendFiltersForContentType(aFilePicker, aContentType, aFileExtension, aSaveMode)
  533. {
  534.   var bundle = getStringBundle();
  535.   // The bundle name for saving only a specific content type.
  536.   var bundleName;
  537.   // The corresponding filter string for a specific content type.
  538.   var filterString;
  539.  
  540.   // XXX all the cases that are handled explicitly here MUST be handled
  541.   // in GetSaveModeForContentType to return a non-fileonly filter.
  542.   switch (aContentType) {
  543.   case "text/html":
  544.     bundleName   = "WebPageHTMLOnlyFilter";
  545.     filterString = "*.htm; *.html";
  546.     break;
  547.  
  548.   case "application/xhtml+xml":
  549.     bundleName   = "WebPageXHTMLOnlyFilter";
  550.     filterString = "*.xht; *.xhtml";
  551.     break;
  552.  
  553.   case "image/svg+xml":
  554.     bundleName   = "WebPageSVGOnlyFilter";
  555.     filterString = "*.svg; *.svgz";
  556.     break;
  557.  
  558.   case "text/xml":
  559.   case "application/xml":
  560.     bundleName   = "WebPageXMLOnlyFilter";
  561.     filterString = "*.xml";
  562.     break;
  563.  
  564.   default:
  565.     if (aSaveMode != SAVEMODE_FILEONLY)
  566.       throw "Invalid save mode for type '" + aContentType + "'";
  567.  
  568.     var mimeInfo = getMIMEInfoForType(aContentType, aFileExtension);
  569.     if (mimeInfo) {
  570.  
  571.       var extEnumerator = mimeInfo.getFileExtensions();
  572.  
  573.       var extString = "";
  574.       while (extEnumerator.hasMore()) {
  575.         var extension = extEnumerator.getNext();
  576.         if (extString)
  577.           extString += "; ";    // If adding more than one extension,
  578.                                 // separate by semi-colon
  579.         extString += "*." + extension;
  580.       }
  581.  
  582.       if (extString)
  583.         aFilePicker.appendFilter(mimeInfo.description, extString);
  584.     }
  585.  
  586.     break;
  587.   }
  588.  
  589.   if (aSaveMode & SAVEMODE_COMPLETE_DOM) {
  590.     aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), filterString);
  591.     // We should always offer a choice to save document only if
  592.     // we allow saving as complete.
  593.     aFilePicker.appendFilter(bundle.GetStringFromName(bundleName), filterString);
  594.   }
  595.  
  596.   if (aSaveMode & SAVEMODE_COMPLETE_TEXT)
  597.     aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
  598.  
  599.   // Always append the all files (*) filter
  600.   aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
  601. }
  602.  
  603. function getPostData()
  604. {
  605.   try {
  606.     var sessionHistory = getWebNavigation().sessionHistory;
  607.     return sessionHistory.getEntryAtIndex(sessionHistory.index, false)
  608.                          .QueryInterface(Components.interfaces.nsISHEntry)
  609.                          .postData;
  610.   }
  611.   catch (e) {
  612.   }
  613.   return null;
  614. }
  615.  
  616. function getStringBundle()
  617. {
  618.   const bundleURL = "chrome://global/locale/contentAreaCommands.properties";
  619.  
  620.   const sbsContractID = "@mozilla.org/intl/stringbundle;1";
  621.   const sbsIID = Components.interfaces.nsIStringBundleService;
  622.   const sbs = Components.classes[sbsContractID].getService(sbsIID);
  623.  
  624.   const lsContractID = "@mozilla.org/intl/nslocaleservice;1";
  625.   const lsIID = Components.interfaces.nsILocaleService;
  626.   const ls = Components.classes[lsContractID].getService(lsIID);
  627.   var appLocale = ls.getApplicationLocale();
  628.   return sbs.createBundle(bundleURL, appLocale);
  629. }
  630.  
  631. function makeWebBrowserPersist()
  632. {
  633.   const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
  634.   const persistIID = Components.interfaces.nsIWebBrowserPersist;
  635.   return Components.classes[persistContractID].createInstance(persistIID);
  636. }
  637.  
  638. /**
  639.  * Constructs a new URI, using nsIIOService.
  640.  * @param aURL The URI spec.
  641.  * @param aOriginCharset The charset of the URI.
  642.  * @param aBaseURI Base URI to resolve aURL, or null.
  643.  * @return an nsIURI object based on aURL.
  644.  */
  645. function makeURI(aURL, aOriginCharset, aBaseURI)
  646. {
  647.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  648.                             .getService(Components.interfaces.nsIIOService);
  649.   return ioService.newURI(aURL, aOriginCharset, aBaseURI);
  650. }
  651.  
  652. function makeFileURI(aFile)
  653. {
  654.   var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  655.                             .getService(Components.interfaces.nsIIOService);
  656.   return ioService.newFileURI(aFile);
  657. }
  658.  
  659. function makeFilePicker()
  660. {
  661.   const fpContractID = "@mozilla.org/filepicker;1";
  662.   const fpIID = Components.interfaces.nsIFilePicker;
  663.   return Components.classes[fpContractID].createInstance(fpIID);
  664. }
  665.  
  666. function getMIMEService()
  667. {
  668.   const mimeSvcContractID = "@mozilla.org/mime;1";
  669.   const mimeSvcIID = Components.interfaces.nsIMIMEService;
  670.   const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
  671.   return mimeSvc;
  672. }
  673.  
  674. // Given aFileName, find the fileName without the extension on the end.
  675. function getFileBaseName(aFileName)
  676. {
  677.   // Remove the file extension from aFileName:
  678.   return aFileName.replace(/\.[^.]*$/, "");
  679. }
  680.  
  681. function getMIMETypeForURI(aURI)
  682. {
  683.   try {
  684.     return getMIMEService().getTypeFromURI(aURI);
  685.   }
  686.   catch (e) {
  687.   }
  688.   return null;
  689. }
  690.  
  691. function getMIMEInfoForType(aMIMEType, aExtension)
  692. {
  693.   if (aMIMEType || aExtension) {
  694.     try {
  695.       return getMIMEService().getFromTypeAndExtension(aMIMEType, aExtension);
  696.     }
  697.     catch (e) {
  698.     }
  699.   }
  700.   return null;
  701. }
  702.  
  703. function getDefaultFileName(aDefaultFileName, aURI, aDocument,
  704.                             aContentDisposition)
  705. {
  706.   // 1) look for a filename in the content-disposition header, if any
  707.   if (aContentDisposition) {
  708.     const mhpContractID = "@mozilla.org/network/mime-hdrparam;1";
  709.     const mhpIID = Components.interfaces.nsIMIMEHeaderParam;
  710.     const mhp = Components.classes[mhpContractID].getService(mhpIID);
  711.     var dummy = { value: null };  // Need an out param...
  712.     var charset = getCharsetforSave(aDocument);
  713.  
  714.     var fileName = null;
  715.     try {
  716.       fileName = mhp.getParameter(aContentDisposition, "filename", charset,
  717.                                   true, dummy);
  718.     }
  719.     catch (e) {
  720.       try {
  721.         fileName = mhp.getParameter(aContentDisposition, "name", charset, true,
  722.                                     dummy);
  723.       }
  724.       catch (e) {
  725.       }
  726.     }
  727.     if (fileName)
  728.       return fileName;
  729.   }
  730.  
  731.   try {
  732.     var url = aURI.QueryInterface(Components.interfaces.nsIURL);
  733.     if (url.fileName != "") {
  734.       // 2) Use the actual file name, if present
  735.       var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  736.                                    .getService(Components.interfaces.nsITextToSubURI);
  737.       return validateFileName(textToSubURI.unEscapeURIForUI(url.originCharset || "UTF-8", url.fileName));
  738.     }
  739.   } catch (e) {
  740.     // This is something like a data: and so forth URI... no filename here.
  741.   }
  742.  
  743.   if (aDocument) {
  744.     var docTitle = validateFileName(aDocument.title).replace(/^\s+|\s+$/g, "");
  745.     if (docTitle) {
  746.       // 3) Use the document title
  747.       return docTitle;
  748.     }
  749.   }
  750.  
  751.   if (aDefaultFileName)
  752.     // 4) Use the caller-provided name, if any
  753.     return validateFileName(aDefaultFileName);
  754.  
  755.   // 5) If this is a directory, use the last directory name
  756.   var path = aURI.path.match(/\/([^\/]+)\/$/);
  757.   if (path && path.length > 1)
  758.     return validateFileName(path[1]);
  759.  
  760.   try {
  761.     if (aURI.host)
  762.       // 6) Use the host.
  763.       return aURI.host;
  764.   } catch (e) {
  765.     // Some files have no information at all, like Javascript generated pages
  766.   }
  767.   try {
  768.     // 7) Use the default file name
  769.     return getStringBundle().GetStringFromName("DefaultSaveFileName");
  770.   } catch (e) {
  771.     //in case localized string cannot be found
  772.   }
  773.   // 8) If all else fails, use "index"
  774.   return "index";
  775. }
  776.  
  777. function validateFileName(aFileName)
  778. {
  779.   var re = /[\/]+/g;
  780.   if (navigator.appVersion.indexOf("Windows") != -1) {
  781.     re = /[\\\/\|]+/g;
  782.     aFileName = aFileName.replace(/[\"]+/g, "'");
  783.     aFileName = aFileName.replace(/[\*\:\?]+/g, " ");
  784.     aFileName = aFileName.replace(/[\<]+/g, "(");
  785.     aFileName = aFileName.replace(/[\>]+/g, ")");
  786.   }
  787.   else if (navigator.appVersion.indexOf("Macintosh") != -1)
  788.     re = /[\:\/]+/g;
  789.   
  790.   return aFileName.replace(re, "_");
  791. }
  792.  
  793. function getNormalizedLeafName(aFile, aDefaultExtension)
  794. {
  795.   if (!aDefaultExtension)
  796.     return aFile;
  797.  
  798. //@line 837 "/e/xr19rel/WINNT_5.2_Depend/mozilla/toolkit/content/contentAreaUtils.js"
  799.   // Remove trailing dots and spaces on windows
  800.   aFile = aFile.replace(/[\s.]+$/, "");
  801. //@line 840 "/e/xr19rel/WINNT_5.2_Depend/mozilla/toolkit/content/contentAreaUtils.js"
  802.  
  803.   // Remove leading dots
  804.   aFile = aFile.replace(/^\.+/, "");
  805.  
  806.   // Fix up the file name we're saving to to include the default extension
  807.   var i = aFile.lastIndexOf(".");
  808.   if (aFile.substr(i + 1) != aDefaultExtension)
  809.     return aFile + "." + aDefaultExtension;
  810.  
  811.   return aFile;
  812. }
  813.  
  814. function getDefaultExtension(aFilename, aURI, aContentType)
  815. {
  816.   if (aContentType == "text/plain" || aContentType == "application/octet-stream" || aURI.scheme == "ftp")
  817.     return "";   // temporary fix for bug 120327
  818.  
  819.   // First try the extension from the filename
  820.   const stdURLContractID = "@mozilla.org/network/standard-url;1";
  821.   const stdURLIID = Components.interfaces.nsIURL;
  822.   var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
  823.   url.filePath = aFilename;
  824.  
  825.   var ext = url.fileExtension;
  826.  
  827.   // This mirrors some code in nsExternalHelperAppService::DoContent
  828.   // Use the filename first and then the URI if that fails
  829.  
  830.   var mimeInfo = getMIMEInfoForType(aContentType, ext);
  831.  
  832.   if (ext && mimeInfo && mimeInfo.extensionExists(ext))
  833.     return ext;
  834.  
  835.   // Well, that failed.  Now try the extension from the URI
  836.   var urlext;
  837.   try {
  838.     url = aURI.QueryInterface(Components.interfaces.nsIURL);
  839.     urlext = url.fileExtension;
  840.   } catch (e) {
  841.   }
  842.  
  843.   if (urlext && mimeInfo && mimeInfo.extensionExists(urlext)) {
  844.     return urlext;
  845.   }
  846.   else {
  847.     try {
  848.       if (mimeInfo)
  849.         return mimeInfo.primaryExtension;
  850.     }
  851.     catch (e) {
  852.     }
  853.     // Fall back on the extensions in the filename and URI for lack
  854.     // of anything better.
  855.     return ext || urlext;
  856.   }
  857. }
  858.  
  859. function GetSaveModeForContentType(aContentType)
  860. {
  861.   var saveMode = SAVEMODE_FILEONLY;
  862.   switch (aContentType) {
  863.   case "text/html":
  864.   case "application/xhtml+xml":
  865.   case "image/svg+xml":
  866.     saveMode |= SAVEMODE_COMPLETE_TEXT;
  867.     // Fall through
  868.   case "text/xml":
  869.   case "application/xml":
  870.     saveMode |= SAVEMODE_COMPLETE_DOM;
  871.     break;
  872.   }
  873.  
  874.   return saveMode;
  875. }
  876.  
  877. function getCharsetforSave(aDocument)
  878. {
  879.   if (aDocument)
  880.     return aDocument.characterSet;
  881.  
  882.   if (document.commandDispatcher.focusedWindow)
  883.     return document.commandDispatcher.focusedWindow.document.characterSet;
  884.  
  885.   return window.content.document.characterSet;
  886. }
  887.  
  888. /**
  889.  * Open a URL from chrome, determining if we can handle it internally or need to
  890.  *  launch an external application to handle it.
  891.  * @param aURL The URL to be opened
  892.  */
  893. function openURL(aURL)
  894. {
  895.   var ios = Components.classes["@mozilla.org/network/io-service;1"]
  896.                       .getService(Components.interfaces.nsIIOService);
  897.   var uri = ios.newURI(aURL, null, null);
  898.  
  899.   var protocolSvc = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
  900.                               .getService(Components.interfaces.nsIExternalProtocolService);
  901.  
  902.   if (!protocolSvc.isExposedProtocol(uri.scheme)) {
  903.     // If we're not a browser, use the external protocol service to load the URI.
  904.     protocolSvc.loadUrl(uri);
  905.   }
  906.   else {
  907.     var loadgroup = Components.classes["@mozilla.org/network/load-group;1"]
  908.                               .createInstance(Components.interfaces.nsILoadGroup);
  909.     var appstartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
  910.                                .getService(Components.interfaces.nsIAppStartup);
  911.  
  912.     var loadListener = {
  913.       onStartRequest: function ll_start(aRequest, aContext) {
  914.         appstartup.enterLastWindowClosingSurvivalArea();
  915.       },
  916.       onStopRequest: function ll_stop(aRequest, aContext, aStatusCode) {
  917.         appstartup.exitLastWindowClosingSurvivalArea();
  918.       },
  919.       QueryInterface: function ll_QI(iid) {
  920.         if (iid.equals(Components.interfaces.nsISupports) ||
  921.             iid.equals(Components.interfaces.nsIRequestObserver) ||
  922.             iid.equals(Components.interfaces.nsISupportsWeakReference))
  923.           return this;
  924.         throw Components.results.NS_ERROR_NO_INTERFACE;
  925.       }
  926.     }
  927.     loadgroup.groupObserver = loadListener;
  928.  
  929.     var uriListener = {
  930.       onStartURIOpen: function(uri) { return false; },
  931.       doContent: function(ctype, preferred, request, handler) { return false; },
  932.       isPreferred: function(ctype, desired) { return false; },
  933.       canHandleContent: function(ctype, preferred, desired) { return false; },
  934.       loadCookie: null,
  935.       parentContentListener: null,
  936.       getInterface: function(iid) {
  937.         if (iid.equals(Components.interfaces.nsIURIContentListener))
  938.           return this;
  939.         if (iid.equals(Components.interfaces.nsILoadGroup))
  940.           return loadgroup;
  941.         throw Components.results.NS_ERROR_NO_INTERFACE;
  942.       }
  943.     }
  944.  
  945.     var channel = ios.newChannelFromURI(uri);
  946.     var uriLoader = Components.classes["@mozilla.org/uriloader;1"]
  947.                               .getService(Components.interfaces.nsIURILoader);
  948.     uriLoader.openURI(channel, true, uriListener);
  949.   }
  950. }
  951.